///************************************************************************
/// <copyrigth>2018-2019 Corporation.All Rights Reserved</copyrigth>
/// <author>tangyingzhong</author>
/// <contact>tangyz114987@outlook.com</contact>
/// <version>v1.0.0</version>
/// <describe>
/// It can be used betwwen producer and customer as a common cache
///</describe>
/// <date>2019/7/16</date>
///***********************************************************************
#ifndef CIRCLEBUFFER_H
#define CIRCLEBUFFER_H

#include <atomic>
#include <string>
#include <vector>
#include "Log/SystemLog.h"

namespace SystemTool
{
	namespace Buffer
	{
		template <class T>
		class CircleBuffer
		{
		public:
			typedef void None;
			typedef bool Boolean;
			typedef void* Object;
			typedef char Byte;
			typedef Byte* ByteArray;
			typedef unsigned int Length;
			typedef unsigned int Index;
			typedef T ValueType;
			typedef ValueType* Pointer;
			typedef ValueType& Reference;

		public:
			// Construct the CircleBuffer
			CircleBuffer(Length iBufferSize = 1024) :
				m_WritePos(0),
				m_ReadPos(0),
				m_Disposed(false)
			{
				Initialize(iBufferSize);
			}

			// Detructe the CircleBuffer
			~CircleBuffer()
			{
				Destory();
			}

		private:
			// Allow the object copying
			CircleBuffer(const CircleBuffer& other) {}

			// Allow the object assignment
			CircleBuffer& operator=(const CircleBuffer& other) { return *this; }

		public:
			// Get the data automatically
			Boolean Fetch(Reference data)
			{
				Boolean bSuccess = false;

				// To know wether the buffer is None
				if (IsEmpty() == true)
				{
					LOG_STD("Buffer is empty!");

					return bSuccess;
				}

				// Get the current reading position
				Index iReadPos = GetReadPos() & (GetBufferSize() - 1);

				// Get the data at the position
				bSuccess = Read(iReadPos, data);

				// Change the read pos
				IncreaseReadPos();

				return bSuccess;
			}

			// Push the data automatically
			Boolean Push(Reference data)
			{
				Boolean bSuccess = false;

				// To know wether the buffer is full
				if (IsFull() == true)
				{
					LOG_STD("Buffer is full!");

					return bSuccess;
				}

				// Get the current writing position
				Index iWritePos = GetWritePos() & (GetBufferSize() - 1);

				// Write the data
				bSuccess = Write(iWritePos, data);

				// Change the write pos
				IncreaseWritePos();

				return bSuccess;
			}

			// Clear the buffer
			None Clear()
			{
				SetReadPos(0);

				SetWritePos(0);

				std::vector<ValueType>().swap(m_CircleBuffer);
			}

		private:
			// Initialize the CircleBuffer
			None Initialize(Length iBufferSize)
			{
				// Set the buffer size
				SetBufferSize(iBufferSize);

				// Create the array
				CreateBuffer(iBufferSize);

				// Set the read position
				SetReadPos(0);

				// Set the write position
				SetWritePos(0);
			}

			// Destory the CircleBuffer
			None Destory()
			{
				if (!GetDisposed())
				{
					SetDisposed(true);

					// Clear the buffer
					ClearBuffer();
				}
			}

			// Create the array
			None CreateBuffer(int iBufferSize)
			{
				m_CircleBuffer.resize(iBufferSize);

				std::memset(m_CircleBuffer.data(), 0, iBufferSize);
			}

			// Clear the buffer
			None ClearBuffer()
			{
				m_CircleBuffer.clear();
			}

			// Is the buffer full
			Boolean IsFull()
			{
				if (GetWritePos() - GetReadPos() == GetBufferSize())
				{
					return true;
				}

				return false;
			}

			// Is the buffer None
			Boolean IsEmpty()
			{
				if (GetReadPos() - GetWritePos() == 0)
				{
					return true;
				}

				return false;
			}

			// Increase the read position
			None IncreaseReadPos()
			{
				++m_ReadPos;
			}

			// Increase the write position
			None IncreaseWritePos()
			{
				++m_WritePos;
			}

			// Read the data from the exactly position
			Boolean Read(Index iPos, Reference data)
			{
				Boolean bSuccess = false;

				// Check the position's legal
				if (iPos < 0 || iPos > (GetBufferSize() - 1))
				{
					return bSuccess;
				}

				// Get the data
				data = m_CircleBuffer[iPos];

				bSuccess = true;

				return bSuccess;
			}

			// Write the data from the exactly position
			Boolean Write(Index iPos, Reference data)
			{
				Boolean bSuccess = false;

				// Check the position's legal
				if (iPos < 0 || iPos > (GetBufferSize() - 1))
				{
					return bSuccess;
				}

				// Get the data
				m_CircleBuffer[iPos] = data;

				bSuccess = true;

				return bSuccess;
			}

		private:
			// Get the write pos of buffer 
			inline Index GetWritePos() const
			{
				return m_WritePos;
			}

			// Set the write position
			inline None SetWritePos(Index iWritePos)
			{
				m_WritePos = iWritePos;
			}

			// Get the read position
			inline Index GetReadPos() const
			{
				return m_ReadPos;
			}

			// Set the read position
			inline None SetReadPos(Index iReadPos)
			{
				m_ReadPos = iReadPos;
			}

			// Set the buffer size
			inline None SetBufferSize(Length iBufferSize)
			{
				m_BufferSize = iBufferSize;
			}

			// Get the buffer size
			inline Length GetBufferSize() const
			{
				return m_BufferSize;
			}

			// Get the disposed
			inline Boolean GetDisposed() const
			{
				return m_Disposed;
			}

			// Set the disposed	
			inline None SetDisposed(Boolean bDisposed)
			{
				m_Disposed = bDisposed;
			}

		private:
			// Circle buffer
			std::vector<ValueType> m_CircleBuffer;

			// Buffer size
			Length m_BufferSize;

			// Write position
			std::atomic<unsigned int> m_WritePos;

			// Read position
			std::atomic<unsigned int> m_ReadPos;

			// Disposed status
			Boolean m_Disposed;
		};
	}
}

#endif // CIRCLEBUFFER_H
